package com.xiaoy.javamaildemo.formwork.javamail;

import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IdleManager;
import com.xiaoy.javamaildemo.JavaMailConf;
import com.xiaoy.javamaildemo.enums.FolderTypeEnum;
import com.xiaoy.javamaildemo.enums.ProtocolTypeEnum;
import lombok.SneakyThrows;
import org.springframework.util.Assert;

import javax.mail.*;
import javax.mail.event.MessageCountAdapter;
import javax.mail.event.MessageCountEvent;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.search.SearchTerm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName MailContext
 * @Description TODO
 * @Author xiaoying
 * @Date 2022/6/27 14:04
 * @Version V1.0.0
 */
public class MailContext extends AbstractMailContext implements IMail{

    public MailContext(JavaMailConf javaMailConf) throws Exception {
        this(javaMailConf,null);
    }

    public MailContext(JavaMailConf javaMailConf, Authenticator authenticator) throws Exception {
        this.javaMailConf = javaMailConf;
        this.session = Session.getInstance(javaMailConf.getProps(),authenticator);
        this.transport = session.getTransport();
        this.imapStore = session.getStore(javaMailConf.getImapProtocol());
        this.pop3Store = session.getStore(javaMailConf.getPop3Protocol());
    }

    @Override
    public VevorMimeMessageBo doGetMessage(FolderTypeEnum folderTypeEnum, long messageNum) throws Exception {
        IMAPFolder folder = (IMAPFolder) findFolder(folderTypeEnum, ProtocolTypeEnum.IMAP);
        Message message = folder.getMessageByUID(messageNum);
        VevorMimeMessageBo vevorMimeMessage = new VevorMimeMessageBo();
        vevorMimeMessage.setMessageId(messageNum);
        vevorMimeMessage.setSubject(message.getSubject());
        vevorMimeMessage.setSendAddress(((InternetAddress)message.getFrom()[0]).getAddress());
        List<Address> addresses = Arrays.asList(message.getAllRecipients());
        List<String> objects = new ArrayList<>();
        for (Address address : addresses) {
            String address1 = ((InternetAddress) address).getAddress();
            objects.add(address1);
        }
        vevorMimeMessage.setRecipients(objects);
        return vevorMimeMessage;
    }

    @Override
    public List<VevorMimeMessageBo> doPull(FolderTypeEnum folderTypeEnum, SearchTerm searchTerm) throws Exception {
        IMAPFolder folder = (IMAPFolder) findFolder(folderTypeEnum, ProtocolTypeEnum.IMAP);
        Message[] search;
        if(null != searchTerm){
            search = folder.search(searchTerm);
        }
        search = folder.getMessages();
        List<Message> messages = Arrays.asList(search);
        List<VevorMimeMessageBo> vevorMimeMessagesList = new ArrayList(messages.size());
        for (Message message : messages) {
            VevorMimeMessageBo vevorMimeMessage = new VevorMimeMessageBo();
            vevorMimeMessage.setMessageId(folder.getUID(message));
            vevorMimeMessage.setSubject(message.getSubject());
            vevorMimeMessage.setSendAddress(((InternetAddress)message.getFrom()[0]).getAddress());
            List<Address> addresses = Arrays.asList(message.getAllRecipients());
            List<String> objects = new ArrayList<>();
            for (Address address : addresses) {
                String address1 = ((InternetAddress) address).getAddress();
                objects.add(address1);
            }
            vevorMimeMessage.setRecipients(objects);
            vevorMimeMessagesList.add(vevorMimeMessage);
        }
        return vevorMimeMessagesList;
    }

    @Override
    public void doSendMail(MimeMessage mimeMessage) throws Exception {
        transport.connect(javaMailConf.getUserName(),javaMailConf.getPassword());
        transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());
    }

    @Override
    public Long doCreateDrafts(MimeMessage mimeMessage, MessageCountAdapter messageCountAdapter) throws Exception {
        IMAPFolder folder = (IMAPFolder) findFolder(FolderTypeEnum.DRAFT,ProtocolTypeEnum.IMAP);
        mimeMessage.setFlag(Flags.Flag.DRAFT,true);
        MimeMessage[] messages = {mimeMessage};
        ExecutorService es = Executors.newCachedThreadPool();
        IdleManager idleManager = new IdleManager(session, es);
        final Integer[] draftId = {null};
        folder.addMessageCountListener(new MessageCountAdapter() {
            @SneakyThrows
            @Override
            public void messagesAdded(MessageCountEvent e) {
                Message[] messages1 = e.getMessages();
                int messageNumber = messages1[0].getMessageNumber();
                draftId[0] = new Integer(messageNumber);
            }
        });
        folder.appendMessages(messages);
        idleManager.watch(folder);
        long uid = folder.getUID(folder.getMessage(draftId[0]));
        return uid;
    }

    @Override
    protected void doDeleteDrafts(long draftId) throws Exception {
        IMAPFolder folder = (IMAPFolder) findFolder(FolderTypeEnum.DRAFT,ProtocolTypeEnum.IMAP);
        Message message = folder.getMessageByUID(draftId);
        checkMessageId(message,draftId);
        message.setFlag(Flags.Flag.DELETED,true);
    }



    @Override
    public synchronized Long doUpdateDrafts(MimeMessage mimeMessage,long draftId) throws Exception {
        IMAPFolder folder = (IMAPFolder) findFolder(FolderTypeEnum.DRAFT,ProtocolTypeEnum.IMAP);
        Message message = folder.getMessageByUID(draftId);
        checkMessageId(message,draftId);
        message.setFlag(Flags.Flag.DELETED,true);
        return doCreateDrafts(mimeMessage,null);
    }
    @Override
    public void doSendDraft(long draftId) throws Exception {
        IMAPFolder folder = (IMAPFolder) findFolder(FolderTypeEnum.DRAFT,ProtocolTypeEnum.IMAP);
        Message message = folder.getMessageByUID(draftId);
        transport.connect(javaMailConf.getUserName(),javaMailConf.getPassword());
        transport.sendMessage(message,message.getAllRecipients());
        message.setFlag(Flags.Flag.DELETED,true);
    }
    @Override
    public void doReply(long messageId,MimeMessage mimeMessage) throws Exception{
        IMAPFolder folder = (IMAPFolder) findFolder(null,ProtocolTypeEnum.IMAP);
        Message message = folder.getMessageByUID(messageId);
        checkMessageId(message,messageId);
        Message reply = message.reply(true);
        copyTo(reply,mimeMessage);
        reply.saveChanges();
        sendMail((MimeMessage) reply);
    }

    @Override
    public Long doCreateDraftReply(long messageId,MimeMessage mimeMessage) throws Exception{
        IMAPFolder folder = (IMAPFolder) findFolder(null,ProtocolTypeEnum.IMAP);
        Message message = folder.getMessageByUID(messageId);
        checkMessageId(message,messageId);
        Message reply = message.reply(true);
        copyTo(reply,mimeMessage);
        reply.setFlag(Flags.Flag.DRAFT,true);
        reply.saveChanges();
        return doCreateDrafts((MimeMessage) reply,null);
    }

    private void copyTo(Message message, MimeMessage mimeMessage) throws Exception {
        if (mimeMessage.getFrom() != null) {
            message.addFrom(mimeMessage.getFrom());
        }

        if (mimeMessage.getReplyTo() != null) {
            message.setReplyTo(mimeMessage.getReplyTo());
        }

        if (mimeMessage.getSentDate() != null) {
            message.setSentDate(mimeMessage.getSentDate());
        }

        if (mimeMessage.getSubject() != null) {
            message.setSubject(mimeMessage.getSubject());
        }

        if (mimeMessage.getContent() != null) {
            Object content = mimeMessage.getContent();
            if(content instanceof String){
                message.setContent(content,"text/html;charset=UTF-8");
            }else{
                message.setContent((Multipart) mimeMessage.getContent());
            }
        }
    }

    private Folder findFolder(FolderTypeEnum folderTypeEnum,ProtocolTypeEnum protocolTypeEnum) throws Exception {
        Folder folder = null;
        if(protocolTypeEnum.equals(ProtocolTypeEnum.IMAP)){
            if(!imapStore.isConnected()){
                imapStore.connect(javaMailConf.getImapHost(),javaMailConf.getImapPort(),javaMailConf.getUserName(),javaMailConf.getPassword());
            }
            folder = imapStore.getFolder(null != folderTypeEnum ? folderTypeEnum.getCode() : FolderTypeEnum.INBOX.getCode());
        }else{
            if(!pop3Store.isConnected()){
                pop3Store.connect(javaMailConf.getPop3Host(),javaMailConf.getPop3Port(),javaMailConf.getUserName(),javaMailConf.getPassword());
            }
            folder = pop3Store.getFolder(null != folderTypeEnum ? folderTypeEnum.getCode() : FolderTypeEnum.INBOX.getCode());
        }
        folder.open(Folder.READ_WRITE);
        return folder;
    }
    private void checkMessageId(Message message, long messageId) {
        Assert.isTrue(null != message,"messageId:"+messageId+" is invalid");
    }

}
